﻿using Grow.Domain.EventBus;
using Grow.EventBus.Cap.PublisherDomainEvents;
using Grow.Module.Service;
using Grow.Scheduler.Job;

namespace Grow.EventBus.Cap.Jobs
{
    public class RetryPublisherDomainEventJob : JobBase
    {
        private readonly DomainEventBusOptions _eventBusOptions;
        private readonly IPublisherDomainEventHandler _publisherHandler;
        private readonly IPublisherDomainEventRepository _publisherDomainEventRepository;
        public RetryPublisherDomainEventJob(IServiceContext serviceContext) : base(serviceContext)
        {
            _eventBusOptions = serviceContext.GetOptions<DomainEventBusOptions>();

            _publisherHandler = _serviceContext.GetRequiredService<IPublisherDomainEventHandler>();

            _publisherDomainEventRepository = serviceContext.GetRequiredService<IPublisherDomainEventRepository>();
        }

        public override async Task ExecuteAsync(CancellationToken cancellationToken)
        {
            var publisherDomainEventDtos = await GetPublisherDomainEventsOfNeedRetryAsync(cancellationToken);
            if (publisherDomainEventDtos.Count == 0)
            {
                return;
            }
            Parallel.ForEach(publisherDomainEventDtos, publisherDomainEventDto =>
            {
                _ = Task.Factory.StartNew(async () =>
                {
                    await _publisherHandler.PublisherAsync(publisherDomainEventDto, cancellationToken);
                }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default);
            });
        }
        private async Task<List<PublisherDomainEventDto>> GetPublisherDomainEventsOfNeedRetryAsync(CancellationToken cancellationToken)
        {
            var now = DateTimeOffset.Now;
            var sendAt = now.AddSeconds(-_eventBusOptions.StartRetryAfter);

            var publisherEventDataEntitys = await _publisherDomainEventRepository.ToListAsync(a =>
                a.SendAt < sendAt
                && a.EnvironmentName == _eventBusOptions.EnvironmentName
                && a.RetryCount < _eventBusOptions.RetryLimitCount
                && (a.LockEndTime < now || a.LockEndTime == null)
                && (a.EventStatus == PublisherDomainEventStatus.Wait || a.EventStatus == PublisherDomainEventStatus.Fail)
               ,_eventBusOptions.RetryFailedMax,cancellationToken);

            return publisherEventDataEntitys.Select(a =>
            new PublisherDomainEventDto(
                a.Id,
                a.EventName,
                a.EventBody,
                a.EventStatus,
                a.RetryCount,
                a.SendAt,
                a.LockEndTime,
                a.ExpireTime)).ToList();
        }
    }
}
